package cn.anzhiyublog.staranimegallery.service.impl;

import cn.anzhiyublog.staranimegallery.cache.template.RedisCache;
import cn.anzhiyublog.staranimegallery.exception.GalleryException;
import cn.anzhiyublog.staranimegallery.mapper.UserMapper;
import cn.anzhiyublog.staranimegallery.model.domain.Picture;
import cn.anzhiyublog.staranimegallery.model.domain.User;
import cn.anzhiyublog.staranimegallery.model.dto.user.*;
import cn.anzhiyublog.staranimegallery.model.enums.Authority;
import cn.anzhiyublog.staranimegallery.model.enums.Role;
import cn.anzhiyublog.staranimegallery.model.enums.StatusCode;
import cn.anzhiyublog.staranimegallery.model.vo.UserVO;
import cn.anzhiyublog.staranimegallery.service.PictureService;
import cn.anzhiyublog.staranimegallery.service.SettingService;
import cn.anzhiyublog.staranimegallery.service.UserService;
import cn.anzhiyublog.staranimegallery.util.EncryptUtils;
import cn.anzhiyublog.staranimegallery.util.ThrowUtils;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 用户业务层
 *
 * @author 安知一只鱼
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PictureService pictureService;

    @Override
    public long register(RegisterRequest registerRequest) {
        // 账号密码格式校验
        this.validUser(registerRequest.getAccount(),registerRequest.getPassword());
        // 两次密码校验
        ThrowUtils.throwIf(!registerRequest.getPassword().equals(registerRequest.getRePassword()), StatusCode.BAD_REQUEST, "两次密码不一致");
        // 账号重复查询
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getAccount,registerRequest.getAccount());
        ThrowUtils.throwIf(this.baseMapper.exists(queryWrapper), StatusCode.BAD_REQUEST, "账号已存在");
        // 插入到数据库（密码加密存储）
        User user = new User();
        user.setAccount(registerRequest.getAccount());
        user.setPassword(EncryptUtils.md5(registerRequest.getPassword()));
        ThrowUtils.throwIf(!this.save(user), StatusCode.INTERNAL_SERVER_ERROR, "注册失败");
        // 返回注册的id
        return user.getId();
    }

    @Override
    public User login(LoginRequest loginRequest, HttpServletRequest request) {
        // 账号密码格式校验
        this.validUser(loginRequest.getAccount(),loginRequest.getPassword());
        // 比对数据库（密码也要加密）
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getAccount,loginRequest.getAccount())
                .eq(User::getPassword,EncryptUtils.md5(loginRequest.getPassword()));
        User user = this.baseMapper.selectOne(queryWrapper);
        ThrowUtils.throwIf(user==null, StatusCode.BAD_REQUEST, "账号或密码错误");
        // 添加进session
        request.getSession().setAttribute("login_user", user);
        // 返回登录的账号
        return user;
    }

    @Override
    public User getLoginUser(HttpServletRequest request) {
        // 获取session中的用户
        User loginUser = (User) request.getSession().getAttribute("login_user");
        // 空校验
        ThrowUtils.throwIf(loginUser==null, StatusCode.NO_LOGIN, "未登录");
        // 返回数据库数据
        User user = this.getById(loginUser.getId());
        ThrowUtils.throwIf(user==null, StatusCode.NO_LOGIN, "未登录");
        return user;
    }

    @Override
    public void logout(HttpServletRequest request) {
        // 获取session中的用户数据
        User user = (User) request.getSession().getAttribute("login_user");
        // 空校验
        ThrowUtils.throwIf(user==null, StatusCode.NO_LOGIN, "未登录");
        // 注销
        request.getSession().setAttribute("login_user", null);
    }

    @Override
    public UserVO ObjToVO(User user) {
        UserVO userVO = new UserVO();
        BeanUtil.copyProperties(user,userVO);
        return userVO;
    }

    @Override
    public long addUser(UserAddRequest userAddRequest) {
        // 请求转实体
        User user = new User();
        BeanUtil.copyProperties(userAddRequest, user);
        // 默认密码
        final String password = "123123123";
        String encryptPassword = EncryptUtils.md5(password);
        user.setPassword(encryptPassword);
        // 默认用户名（若未填）
        if (user.getNickname().isEmpty()){
            user.setNickname("星轨漫游者"+user.getAccount());
        }
        // 账号校验
        this.validUser(user.getAccount(), password);
        // 存在校验
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getAccount,user.getAccount());
        boolean isExist = this.baseMapper.exists(queryWrapper);
        ThrowUtils.throwIf(isExist, StatusCode.BAD_REQUEST, "账号已存在");
        // 添加进数据
        boolean isSave = this.save(user);
        ThrowUtils.throwIf(!isSave, StatusCode.INTERNAL_SERVER_ERROR, "添加失败");
        return user.getId();
    }

    @Override
    public long addUser(UserAddRequest userAddRequest, User loginUser) {
        // 如果是非管理账号，不允许使用该接口
        Role currentRole = Role.getEnumByCode(loginUser.getRole());
        ThrowUtils.throwIf(currentRole==null || !Authority.inManage(currentRole), StatusCode.FORBIDDEN);
        // 如果是普通管理员，则只能创建非管理账号
        if (currentRole.equals(Role.ADMIN)){
            ThrowUtils.throwIf(Authority.inManage(Role.getEnumByCode(userAddRequest.getRole())), StatusCode.FORBIDDEN, "您无权这么做");
        }
        // 如果是超级管理员，则无限制
        return this.addUser(userAddRequest);
    }

    @Override
    public void deleteUser(UserDeleteRequest userDeleteRequest) {
        Long id = userDeleteRequest.getId();
        ThrowUtils.throwIf(!this.removeById(id), StatusCode.INTERNAL_SERVER_ERROR, "删除失败");
    }

    @Override
    public void deleteUser(UserDeleteRequest userDeleteRequest, User loginUser) {
        // 超级管理员不能被删除，管理员无法删除其他管理员的数据（超级管理员除外）
        User deleteUser = this.getUserById(userDeleteRequest.getId());
        // 比对被删除用户的角色是否为超级管理员
        if (deleteUser.getRole().equals(Role.SUPER.getCode())) {
            throw new GalleryException(StatusCode.FORBIDDEN, "超级管理员不能被删除");
        }
        // 如果删除的是普通管理员，则要校验是否为本人或超级管理员
        if (deleteUser.getRole().equals(Role.ADMIN.getCode())) {
            // 只有当删除的是其他管理员且登录用户不是超级管理员时才抛出异常
            if (!deleteUser.getId().equals(loginUser.getId()) && !loginUser.getRole().equals(Role.SUPER.getCode())) {
                throw new GalleryException(StatusCode.FORBIDDEN, "无法删除其他管理员的数据");
            }
        }
        // 执行真正的删除操作
        this.deleteUser(userDeleteRequest);
    }

    @Override
    public void updateUser(UserUpdateRequest userUpdateRequest) {
        // 实体转换
        User user = new User();
        BeanUtil.copyProperties(userUpdateRequest,user);
        // 校验
        this.validUser(user);
        // 执行修改
        boolean isUpdate = this.updateById(user);
        ThrowUtils.throwIf(!isUpdate, StatusCode.INTERNAL_SERVER_ERROR, "修改失败");
    }

    @Override
    public void updateUser(UserUpdateRequest userUpdateRequest, User loginUser) {
        // 非管理用户无法使用
        ThrowUtils.throwIf(!Authority.inManage(loginUser), StatusCode.FORBIDDEN);
        // 普通管理员只能修改非管理用户
        if (loginUser.getRole().equals(Role.ADMIN.getCode())){
            ThrowUtils.throwIf(Authority.inManage(Role.getEnumByCode(userUpdateRequest.getRole())), StatusCode.FORBIDDEN, "您无权这么做");
        }
        // 超级管理员无限制
        this.updateUser(userUpdateRequest);
    }

    @Override
    public void editUser(UserEditRequest userEditRequest, User loginUser) {
        // 是否为本人
        ThrowUtils.throwIf(!loginUser.getId().equals(userEditRequest.getId()), StatusCode.FORBIDDEN);
        // 实体转换
        User user = new User();
        BeanUtil.copyProperties(userEditRequest,user);
        user.setEditTime(new Date());
        // 格式校验
        this.validUser(user);
        // 执行修改
        boolean isUpdate = this.updateById(user);
        ThrowUtils.throwIf(!isUpdate, StatusCode.INTERNAL_SERVER_ERROR, "修改失败");
    }

    @Override
    public User getUserById(long userID) {
        ThrowUtils.throwIf(userID<0, StatusCode.BAD_REQUEST, "id不能小于0");
        User user = this.getById(userID);
        ThrowUtils.throwIf(user==null, StatusCode.NOT_FOUND, "用户不存在");
        return user;
    }

    @Override
    public Page<User> listUserByPage(UserPageQueryRequest userPageQueryRequest) {
        // 参数校验
        ThrowUtils.throwIf(userPageQueryRequest.getCurrent()<=0, StatusCode.BAD_REQUEST, "页码不能小于等于0");
        // 构造查询语句
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        // id精准查询
        queryWrapper.eq(ObjectUtil.isNotNull(userPageQueryRequest.getId()),User::getId,userPageQueryRequest.getId());
        // 账号精准查询
        queryWrapper.eq(StrUtil.isNotBlank(userPageQueryRequest.getAccount()),User::getAccount,userPageQueryRequest.getAccount());
        // 昵称模糊查询
        queryWrapper.like(StrUtil.isNotBlank(userPageQueryRequest.getNickname()),User::getNickname,userPageQueryRequest.getNickname());
        // 简介模糊查询
        queryWrapper.like(StrUtil.isNotBlank(userPageQueryRequest.getProfile()),User::getProfile,userPageQueryRequest.getProfile());
        // 角色精准查询
        queryWrapper.eq(ObjectUtil.isNotNull(userPageQueryRequest.getRole()),User::getRole,userPageQueryRequest.getRole());
        // 执行分页查询
        return this.page(new Page<>(userPageQueryRequest.getCurrent(), userPageQueryRequest.getSize()), queryWrapper);
    }

    @Override
    public List<UserVO> ObjToVO(List<User> users) {
        return users.stream().map(this::ObjToVO).collect(Collectors.toList());
    }

    @Override
    public void editPassword(UserEditPasswordRequest userEditPasswordRequest, HttpServletRequest request) {
        Long id = userEditPasswordRequest.getId();
        String oldPassword = userEditPasswordRequest.getOldPassword();
        String newPassword = userEditPasswordRequest.getNewPassword();
        String reNewPassword = userEditPasswordRequest.getReNewPassword();
        // 本人校验
        User loginUser = this.getLoginUser(request);
        ThrowUtils.throwIf(!loginUser.getId().equals(userEditPasswordRequest.getId()), StatusCode.FORBIDDEN);
        // 新旧密码校验
        ThrowUtils.throwIf(oldPassword.equals(newPassword), StatusCode.BAD_REQUEST, "新旧密码不能一致");
        // 旧密码校验
        boolean isEquals = EncryptUtils.md5(oldPassword).equals(userMapper.selectPasswordById(id));
        ThrowUtils.throwIf(!isEquals, StatusCode.BAD_REQUEST, "旧密码错误");
        // 新密码校验
        ThrowUtils.throwIf(!newPassword.equals(reNewPassword), StatusCode.BAD_REQUEST, "两次密码不一致");
        // 校验格式
        User user = new User();
        user.setId(id);
        user.setPassword(newPassword);
        this.validUser(user);
        // 加密密码
        user.setPassword(EncryptUtils.md5(newPassword));
        // 添加到数据库中
        boolean isUpdate = this.updateById(user);
        ThrowUtils.throwIf(!isUpdate, StatusCode.INTERNAL_SERVER_ERROR, "修改失败");
    }

    @Override
    public void setAvatar(long userId, long pictureId, User loginUser) {
        // 本人校验
        ThrowUtils.throwIf(userId!=loginUser.getId(), StatusCode.FORBIDDEN);
        // 图片存在校验(如果没有就抛异常，有就返回数据)
        Picture picture = pictureService.getPictureById(pictureId, loginUser);
        // 设置头像
        User user = new User();
        user.setId(userId);
        user.setAvatar(picture.getThumbnailUrl());
        boolean isUpdate = this.updateById(user);
        ThrowUtils.throwIf(!isUpdate, StatusCode.INTERNAL_SERVER_ERROR, "修改失败");
    }

    /**
     * 校验用户
     * @param user 用户
     */
    private void validUser(User user){
        // 账号校验：字数在4-20之间，且字符必须为英文字母和数字
        if (user.getAccount() != null){
            // 账号长度范围
            final int[] accountLengthRange = {4,20};
            // 账号只允许大小写英文字符和数字
            final String regex = "^[a-zA-Z0-9]+$";
            ThrowUtils.throwIf(user.getAccount().length() < accountLengthRange[0] || user.getAccount().length() > accountLengthRange[1], StatusCode.BAD_REQUEST, "账号长度不符合要求");
            // 账号字符校验
            ThrowUtils.throwIf(!Pattern.matches(regex, user.getAccount()), StatusCode.BAD_REQUEST, "账号只能包含大小写英文字符和数字");
        }
        // 密码校验：字数在8-50之间
        if (user.getPassword() != null){
            // 密码长度范围
            final int[] passwordLengthRange = {8,50};
            // 长度校验
            ThrowUtils.throwIf(user.getPassword().length() < passwordLengthRange[0] || user.getPassword().length() > passwordLengthRange[1], StatusCode.BAD_REQUEST, "密码长度不符合要求");
        }
        // 昵称校验：不得多于50字
        if (user.getNickname() != null){
            // 昵称长度范围
            final int maxNicknameLength = 50;
            ThrowUtils.throwIf(user.getNickname().length() > maxNicknameLength, StatusCode.BAD_REQUEST, "昵称长度不符合要求");
        }
        // 简介校验：不得多于200字
        if (user.getProfile() != null){
            // 简介长度范围
            final int maxProfileLength = 200;
            ThrowUtils.throwIf(user.getProfile().length() > maxProfileLength, StatusCode.BAD_REQUEST, "简介长度不符合要求");
        }
        // 角色校验：角色必须存在
        if (user.getRole() != null){
            Role role = Role.getEnumByCode(user.getRole());
            ThrowUtils.throwIf(role==null, StatusCode.BAD_REQUEST, "角色不存在");
        }
    }

    /**
     * 校验用户
     * @param account 账号
     * @param password 密码
     */
    private void validUser(String account, String password) {
        User user = new User();
        user.setAccount(account);
        user.setPassword(password);
        this.validUser(user);
    }
}
